package com.zhuangjie.shop.helper;

import com.zhaungjie.base.utils.LocalDateTimeOperations;
import com.zhuangjie.base.entity.TimeRange;
import com.zhuangjie.shop.vo.TimeItem;
import com.zhuangjie.shop.vo.predetermine.DateItem;
import com.zhuangjie.shop.vo.predetermine.GeneratedPredetermineDayAndTimesInfo;
import com.zhuangjie.shop.vo.predetermine.PredetermineDateAndTime;

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class PredetermineDatesAndTimeRangesInfoGenerator {

    /**
     * 获取立即送达时间LocalDateTime
     * @param predetermineDaysValidPeriod
     * @param makeTime
     * @param deliveryTime
     * @return
     */
    public static LocalDateTime getImmediateDeliveryTime(List<List<TimeRange>> predetermineDaysValidPeriod, Integer makeTime, Integer deliveryTime) {
        // 参照gen方法中的逻辑，我们只需要处理第一天和第一个时间段就可以了
        if (predetermineDaysValidPeriod == null || predetermineDaysValidPeriod.isEmpty() || makeTime == null) {
            throw new RuntimeException("无法生成预订信息，传入的信息不合理！");
        }
        if (deliveryTime == null) {
            deliveryTime = 0;
        }
        LocalDateTimeOperations localDateTimeOperations = new LocalDateTimeOperations();
        final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        for (int i = 0; i < predetermineDaysValidPeriod.size(); i++) {
            List<TimeRange> ranges = predetermineDaysValidPeriod.get(i);
            String lastYmd = LocalDateTime.now().plusDays(i).format(formatter);
            // 先设置YMD部分
            localDateTimeOperations.update( lastYmd,null);
            // 如果是今天，那需要考虑今天过去的时间
            if (i == 0) filterTimeByNowDateTime(ranges);
            for (TimeRange timeRange : ranges) {
                // 设置HMS部分
                localDateTimeOperations.update(null, timeRange.getStartTime());
                localDateTimeOperations.addMinutes(makeTime);
                if (localDateTimeOperations.hmsLt(timeRange.getEndTime()) && lastYmd.equals(localDateTimeOperations.formatDate(formatter))) {
                    // 直接返回我们需要的“尽快送达”的时间
                    return localDateTimeOperations.copyInstance().addMinutes(deliveryTime).getDateOfCore().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                }
            }

        }
        return null;
    }

    /**
     * 生成预订列表展示数据信息
     *
     * @param predetermineDaysValidPeriod 预先确定有效期天数列表（内部是若干营业时间段）
     * @param makeTime                    制作时间
     * @param deliveryTime                配送时间
     * @return {@link GeneratedPredetermineDayAndTimesInfo}
     */

    public static GeneratedPredetermineDayAndTimesInfo gen(List<List<TimeRange>> predetermineDaysValidPeriod, Integer makeTime, Integer deliveryTime) {
        if (predetermineDaysValidPeriod == null || predetermineDaysValidPeriod.isEmpty() || makeTime == null) {
            throw new RuntimeException("无法生成预订信息，传入的信息不合理！");
        }
        int advanceBookingDays = 0;
        if (deliveryTime == null || (advanceBookingDays = predetermineDaysValidPeriod.size()) == 0) deliveryTime = 0;
        GeneratedPredetermineDayAndTimesInfo product = new GeneratedPredetermineDayAndTimesInfo();
        // 加new ArrayList<>在外层是保证是一个可修改的List，否则是不可修改的（修改会报错）
        product.setDatesTimes(new ArrayList<>(Collections.nCopies(predetermineDaysValidPeriod.size(), null)));
        final String formatDateTemplate = "yyyy-MM-dd";
        for (int i = 0; i < advanceBookingDays; i++) {
            LocalDateTimeOperations localDateTimeOperations = new LocalDateTimeOperations().addDay(i);
            // 生成的i天（0是今天）的可选择times
            List<TimeItem> collectTimeItems = new ArrayList<>();
            String lastYmd = localDateTimeOperations.formatDate(formatDateTemplate);
            boolean isExceedingSpecificDay = false;
            // daysAndTimesList可能是空的，当是空的，那collectTimes应该没有的
            if (predetermineDaysValidPeriod.get(i) != null) {
                // 如果是今天，那需要考虑今天过去的时间
                if (i == 0) {
                    filterTimeByNowDateTime(predetermineDaysValidPeriod.get(i));
                }
                for (TimeRange timeRange : predetermineDaysValidPeriod.get(i)) {
                    localDateTimeOperations.update(null, timeRange.getStartTime());
                    while (true) {
                        localDateTimeOperations.addMinutes(makeTime);
                        if ( (isExceedingSpecificDay = !lastYmd.equals(localDateTimeOperations.formatDate(formatDateTemplate))) ||  !localDateTimeOperations.hmsLt(timeRange.getEndTime()) ) {
                            // 这里要保证退出循环后，日期不能因为addMinutes而被改变，后面还需要用到
                            break;
                        }
                        collectTimeItems.add(new TimeItem(localDateTimeOperations.copyInstance().addMinutes(deliveryTime).formatDate("HH:mm")));
                    }
                    // 如果超过了i对应的天数，那就下一天
                    if(isExceedingSpecificDay) {
                        // 将date部分设置回来
                        localDateTimeOperations.update(lastYmd,null);
                        // 如果已经超过了i对应的那天，那就跳过i对应的后面time了
                        break;
                    }
                }
            }
            // 将times组装到day
            product.getDatesTimes().set(i, new DateItem(getMonthDayLabel(i, localDateTimeOperations), lastYmd, collectTimeItems));
        }
        // 过滤掉空头
        filterEmptyHeadDateTime(product.getDatesTimes());
        // 注入“尽快送达” ,即使用immediately代表
        DateItem firstDay = product.getDatesTimes().get(0);
        if (firstDay != null && !firstDay.getTimes().isEmpty()) {
            product.setImmediatePredetermineTime(new PredetermineDateAndTime(firstDay, firstDay.getTimes().get(0)));
        }
        return product;
    }

    /**
     * 按现在日期时间筛选时间
     *
     * @param timeRanges 时间范围
     * @return {@link List}<{@link TimeRange}>
     */
    public static List<TimeRange> filterTimeByNowDateTime(List<TimeRange> timeRanges) {
        Date now = new Date();
        String currentTime = String.format("%02d:%02d:%02d", now.getHours(), now.getMinutes(), now.getSeconds());


        int current = timeToNumber(currentTime);
        List<TimeRange> result = new ArrayList<>();

        for (TimeRange interval : timeRanges) {
            int begin = timeToNumber(interval.getStartTime());
            int end = timeToNumber(interval.getEndTime());

            if (current >= begin && current <= end) {
                interval.setStartTime(currentTime);
                result.add(interval);
            } else if (current < begin) {
                result.add(interval);
            }
        }

        return result;
    }

    private static int timeToNumber(String time) {
        // Helper function to convert time to number
        if (time == null || time.length() == 0 || !time.contains(":")) return 0;
        String[] timeArr = time.split(":");
        return Integer.parseInt(timeArr[0]) * 60 + Integer.parseInt(timeArr[1]);
    }

    public static String getMonthDayLabel(int index, LocalDateTimeOperations timeOperations) {
        String[] indexAndFirstLabel = {"今天", "明天"};
        String dateFormatStr = timeOperations.getYear() == new Date().getYear() ? "MM-dd" : "yyyy-MM-dd";
        String dayOfWeek = timeOperations.getDayOfWeek();

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(timeOperations.getDateOfCore());
        String formattedDate = new SimpleDateFormat(dateFormatStr).format(calendar.getTime());

        String label = indexAndFirstLabel.length - 1 >= index ? indexAndFirstLabel[index] : formattedDate;

        return label + "(" + dayOfWeek + ")";
    }

    public static List<DateItem> filterEmptyHeadDateTime(List<DateItem> dateTimes) {
        if (dateTimes == null || dateTimes.size() == 0) {
            return dateTimes;
        }

        for (int i = 0; i < dateTimes.size(); i++) {
            DateItem times = dateTimes.get(i);
            if (times == null || times.getTimes() == null || times.getTimes().size() == 0) {
                dateTimes.remove(i);
                i--;
            } else {
                break;
            }
        }

        return dateTimes;
    }
}
